#include "HttpConnection.h"
#include<iostream>

HttpConnection::HttpConnection(tcp::socket socket):_socket(std::move(socket))
{

}

/*
* 监听客户端请求，形成读写事件链，但由于是短链接，所以没有形成
* 可以使用协程改进：1.原先注册事件改成开协程----->2.修改异步接口，变成一个可以等待的接口3.回调函数的逻辑放到后面执行4.所有2.3的逻辑放到一个协程中执行，协程的操作放到原先注册事件的地方
* 
* 1.开协程
* 2.协程函数中，调用修改后的接口，等待async_read返回，使用await修饰，等待过程中将主动挂起
* 3.原先回调函数的操作将会放到后面
* 4.监听是否可以获得执行权，交给ioC尽心，只是整体上修改了事件机制，变成了同步机制
*/
void HttpConnection::Start()
{
	auto self = shared_from_this();

	//引用传递，会在里面修改所有的值，发送和接受都使用beast，所以不用担心粘包问题，会被解决
	http::async_read(_socket, _buffer, _request, [self](beast::error_code ec,std::size_t bytes_transferred) {
			try {
				if (ec) {
					std::cout << "http read err is " << ec.what() << std::endl;
					return;
				}

				//处理读到的数据
				boost::ignore_unused(bytes_transferred);
				self->HandleReq();																		//回调函数中用于处理消息，http是短链接，所以不会发生同一个连接下多次读写
				self->CheckDeadline();																	//注册定时器事件，等待定时器事件触发
			}
			catch (std::exception& exp) {
				std::cout << "exception is " << exp.what() << std::endl;
			}
		}
	);
	//异步函数，注册完事件之后直接退出


}


//这里是同步调用逻辑系统，同步调用
//也可以做异步调用，会经过两层：method------>接口，不通过method下，可以有相同的接口，这样两个维度，可以组合出非常多的可调用接口
//后端解析时，首先解析method，然后解析出URL和参数，然后再去调用
void HttpConnection::HandleReq() {
	//设置版本
	_response.version(_request.version());
	//设置为短链接
	_response.keep_alive(false);

	if (_request.method() == http::verb::get) {

		PreParseGetParam();																		//准备参数：如果是get
		bool success = LogicSystem::GetInstance()->HandleGet(_get_url, shared_from_this());		//通过逻辑系统对消息解析，逻辑系统负责所有连接的小西的处理，不仅仅负责当前的客户端，所以要把自己作为指针发送过去
		if (!success) {
			_response.result(http::status::not_found);
			_response.set(http::field::content_type, "text/plain");
			beast::ostream(_response.body()) << "url not found\r\n";

			//写事件
			WriteResponse();
			return;
		}
		_response.result(http::status::ok);
		_response.set(http::field::server, "GateServer");
		WriteResponse();
		return;
	}

	//处理post请求
	//这里不是通过逻辑系统调用发送函数处理，而是逻辑系统处理完成之后，对httpconnect的response做了处理之后
	//回到httpconnection中进行处理
	//这里有一点点不同，之前的是这样操作的，逻辑系统负责处理完成后，直接发送回去，没有返回通过httpconnection进行发送
	//这样有没有影响？
	//首先，这变成了同步操作，所有的回复都必须通过逻辑系统处理完成后，返回回来继续进行操作，不是完全的异步操作，这样就会导致httpconnection正在处理一个消息的同时
	//没有办法对来自客户端的另一条消息进行响应
	//而这也是httpconnection和之前的服务端有所不同的地方


	//httpconnection，只是一条消息一条消息的处理
	//webserver,可以处理多条来自于客户端的消息
	//这就是他们的不同，所以可以如同webserver一样，接受过来的消息存在在队列中
	//然后把队列的消息送到逻辑系统进行处理
	//这样每一条消息都有回应
	if (_request.method() == http::verb::post) 
	{
		//
		bool success = LogicSystem::GetInstance()->HandlePost(_request.target(), shared_from_this());
		if (!success) 
		{
			_response.result(http::status::not_found);
			_response.set(http::field::content_type, "text/plain");
			beast::ostream(_response.body()) << "url not found\r\n";
			WriteResponse();
			return;
		}
		_response.result(http::status::ok);
		_response.set(http::field::server, "GateServer");
		WriteResponse();
		return;
	}
}

/*
* 等待定时器事件，如果定时器事件触发，就关闭连接
*/
void HttpConnection::CheckDeadline() 
{

	//异步等待时间到的事件
	auto self = shared_from_this();

	deadline_.async_wait(
		[self](beast::error_code ec)
		{
			if (!ec)
			{
				// Close socket to cancel any outstanding operation.
				self->_socket.close(ec);
			}
		});
}

/*
* 异步写消息
*/
void HttpConnection::WriteResponse() {
	auto self = shared_from_this();
	_response.content_length(_response.body().size());
	http::async_write(
		_socket,
		_response,
		[self](beast::error_code ec, std::size_t)
		{
			self->_socket.shutdown(tcp::socket::shutdown_send, ec);
			self->deadline_.cancel();								//自己结束，不需要定时器的结束，直接定时器取消
		});
}

void HttpConnection::PreParseGetParam()
{
	// 提取 URI  
	auto uri = _request.target();
	// 查找查询字符串的开始位置（即 '?' 的位置）  
	auto query_pos = uri.find('?');						//所有的都是从0开始计数，找到的就是当前的字符在的位置
	if (query_pos == std::string::npos) {
		_get_url = uri;
		return;
	}

	_get_url = uri.substr(0, query_pos);				//左闭右开
	std::string query_string = uri.substr(query_pos + 1);	//把？去除掉
	std::string key;
	std::string value;
	size_t pos = 0;

	while ((pos = query_string.find('&')) != std::string::npos) {
		auto pair = query_string.substr(0, pos);
		size_t eq_pos = pair.find('=');
		if (eq_pos != std::string::npos) {
			key = UrlDecode(pair.substr(0, eq_pos)); // 假设有 url_decode 函数来处理URL解码  
			value = UrlDecode(pair.substr(eq_pos + 1));
			_get_params[key] = value;
		}
		query_string.erase(0, pos + 1);				
	}
	// 处理最后一个参数对（如果没有 & 分隔符）  
	if (!query_string.empty()) {
		size_t eq_pos = query_string.find('=');
		if (eq_pos != std::string::npos) {
			key = UrlDecode(query_string.substr(0, eq_pos));
			value = UrlDecode(query_string.substr(eq_pos + 1));
			_get_params[key] = value;
		}
	}
}



unsigned char HttpConnection::ToHex(unsigned char x)
{
	return  x > 9 ? x + 55 : x + 48;
}


unsigned char HttpConnection::FromHex(unsigned char x)
{
	unsigned char y;
	if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
	else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
	else if (x >= '0' && x <= '9') y = x - '0';
	else assert(0);
	return y;
}

std::string HttpConnection::UrlEncode(const std::string& str)
{
	std::string strTemp = "";
	size_t length = str.length();
	for (size_t i = 0; i < length; i++)
	{
		//判断是否仅有数字和字母构成
		if (isalnum((unsigned char)str[i]) ||
			(str[i] == '-') ||
			(str[i] == '_') ||
			(str[i] == '.') ||
			(str[i] == '~'))
			strTemp += str[i];
		else if (str[i] == ' ') //为空字符
			strTemp += "+";
		else
		{
			//其他字符需要提前加%并且高四位和低四位分别转为16进制
			strTemp += '%';
			strTemp += ToHex((unsigned char)str[i] >> 4);
			strTemp += ToHex((unsigned char)str[i] & 0x0F);
		}
	}
	return strTemp;
}

std::string HttpConnection::UrlDecode(const std::string& str)
{
	std::string strTemp = "";
	size_t length = str.length();
	for (size_t i = 0; i < length; i++)
	{
		//还原+为空
		if (str[i] == '+') strTemp += ' ';
		//遇到%将后面的两个字符从16进制转为char再拼接
		else if (str[i] == '%')
		{
			assert(i + 2 < length);
			unsigned char high = FromHex((unsigned char)str[++i]);
			unsigned char low = FromHex((unsigned char)str[++i]);
			strTemp += high * 16 + low;
		}
		else strTemp += str[i];
	}
	return strTemp;
}
